#include "MYView.h"

//#include <iostream>

using namespace osg;

MYView* MYView::instance = 0;
// Les membres static non constants doient êtres initialisés dans le .cpp

MYView::MYView()
{
	// Définition des couleurs
	couleurCase = Vec4(193.0/255.0,189.0/255.0,255/255.0,0.5);
	couleurPlateau = Vec4(160.0/255.0,162.0/255.0,172.0/255.0,1);
	couleurRangement = Vec4(193.0/255.0,189.0/255.0,255/255.0,0.5);
	couleurMur = Vec4(0.82,0.60,0.38,1);
	
	rouge = Vec4(0.786,0.33,0.26,0.4);
	bleu = Vec4(0.22,0.58,0.678,0.4);
	blanc = Vec4(243.0/255.0,239.0/255.0,224.0/255.0,1);

	
	nb=MYModel::getInstance()->getSize();
	hc=h_case;
	hb=h_board;
	hw=h_wall;
	sc=t_case;
	ss=t_espacement;
	sb=sc+ss; // case + espacement
	w = nb*sb-ss; // width
	h =sc*(nb+4)+ss*(nb+3); // hauteur

}

MYView *MYView::getInstance()
{
	if (MYView::instance == 0)
		instance = new MYView();
	return instance;
}

ref_ptr<osgViewer::Viewer> MYView::buildSceneGraph(){

	// normal array
	Vec3Array *normalArray = new Vec3Array();
	normalArray->push_back(Vec3(+1, 0, 0));
	normalArray->push_back(Vec3(-1, 0, 0));
	normalArray->push_back(Vec3(0, +1, 0));
	normalArray->push_back(Vec3(0, -1, 0));
	normalArray->push_back(Vec3(0, 0, +1));
	normalArray->push_back(Vec3(0, 0, -1));
	
	ref_ptr<Vec3Array> mur = new Vec3Array;
	//coin bas gauche avant
	mur->push_back(Vec3(+sc/2,0-sb*2+sc/2+ss,0));
	mur->push_back(Vec3(+sc/2,0-sb*2+sc/2+ss,0));
	mur->push_back(Vec3(+sc/2,0-sb*2+sc/2+ss,0));
	//coin bas droit avant
	mur->push_back(Vec3(ss+sc/2,0-sb*2+sc/2+ss,0));
	mur->push_back(Vec3(ss+sc/2,0-sb*2+sc/2+ss,0));
	mur->push_back(Vec3(ss+sc/2,0-sb*2+sc/2+ss,0));
	//coin bas droit arrière
	mur->push_back(Vec3(ss+sc/2,+sc/2,0));
	mur->push_back(Vec3(ss+sc/2,+sc/2,0));
	mur->push_back(Vec3(ss+sc/2,+sc/2,0));
	//coin bas gauche arrière
	mur->push_back(Vec3(+sc/2,+sc/2,0));
	mur->push_back(Vec3(+sc/2,+sc/2,0));
	mur->push_back(Vec3(+sc/2,+sc/2,0));
	//coin haut gauche avant
	mur->push_back(Vec3(+sc/2,0-sb*2+sc/2+ss,h_wall));
	mur->push_back(Vec3(+sc/2,0-sb*2+sc/2+ss,h_wall));
	mur->push_back(Vec3(+sc/2,0-sb*2+sc/2+ss,h_wall));
	//coin haut droit avant
	mur->push_back(Vec3(ss+sc/2,-sb*2+sc/2+ss,h_wall));
	mur->push_back(Vec3(ss+sc/2,-sb*2+sc/2+ss,h_wall));
	mur->push_back(Vec3(ss+sc/2,-sb*2+sc/2+ss,h_wall));
	//coin haut droit arrière
	mur->push_back(Vec3(ss+sc/2,+sc/2,h_wall));
	mur->push_back(Vec3(ss+sc/2,+sc/2,h_wall));
	mur->push_back(Vec3(ss+sc/2,+sc/2,h_wall));
	//coin haut gauche arrière
	mur->push_back(Vec3(+sc/2,+sc/2,h_wall));
	mur->push_back(Vec3(+sc/2,+sc/2,h_wall));
	mur->push_back(Vec3(+sc/2,+sc/2,h_wall));
	

	ref_ptr<Vec4Array> colorArray = new Vec4Array;
	

	
	// Explicite comment il faut utiliser les sommets pour créer la surface requise 
	ref_ptr<DrawElementsUInt> drawElements = new DrawElementsUInt(PrimitiveSet::TRIANGLES,0);
	int i = 0;
	//6*4 -> 4 sommets par face, 6 faces pour un cube
	for(i = 0; i < 6*4; i++){
	colorArray->push_back(couleurMur);
	}
	// bottom
	drawElements->push_back(0); // face 1
	drawElements->push_back(9);
	drawElements->push_back(3);
	drawElements->push_back(9); // face 2
	drawElements->push_back(6);
	drawElements->push_back(3);
	// top
	drawElements->push_back(21);  //face 3
	drawElements->push_back(12);
	drawElements->push_back(18);
	drawElements->push_back(12);  //face 4
	drawElements->push_back(15);
	drawElements->push_back(18);
	// left
	drawElements->push_back(22);  //face 5
	drawElements->push_back(10);
	drawElements->push_back(13);
	drawElements->push_back(10);  //face 6
	drawElements->push_back(1);
	drawElements->push_back(13);
	// right
	drawElements->push_back(16);  //face 7
	drawElements->push_back(4);
	drawElements->push_back(19);
	drawElements->push_back(4);  //face 8
	drawElements->push_back(7);
	drawElements->push_back(19);
	// front
	drawElements->push_back(14);  //face 9
	drawElements->push_back(2);
	drawElements->push_back(17);
	drawElements->push_back(2);   //face 10
	drawElements->push_back(5);
	drawElements->push_back(17);
	// back
	drawElements->push_back(20);  //face 11
	drawElements->push_back(8);
	drawElements->push_back(23);
	drawElements->push_back(8);   //face 12
	drawElements->push_back(11);
	drawElements->push_back(23);

	
	// normal index
	TemplateIndexArray<unsigned int, Array::UIntArrayType, 24, 4> *normalIndexArray;
	normalIndexArray = new TemplateIndexArray<unsigned int, Array::UIntArrayType, 24, 4>();

	// bottom front left					
	normalIndexArray->push_back(5);
	normalIndexArray->push_back(3);
	normalIndexArray->push_back(0);
	// bottom front right
	normalIndexArray->push_back(5);
	normalIndexArray->push_back(2);
	normalIndexArray->push_back(0);
	// bottom back right
	normalIndexArray->push_back(5);
	normalIndexArray->push_back(2);
	normalIndexArray->push_back(1);
	// bottom back left
	normalIndexArray->push_back(5);
	normalIndexArray->push_back(3);
	normalIndexArray->push_back(1);

	// top front left					
	normalIndexArray->push_back(4);
	normalIndexArray->push_back(3);
	normalIndexArray->push_back(0);
	// top front right
	normalIndexArray->push_back(4);
	normalIndexArray->push_back(2);
	normalIndexArray->push_back(0);
	// top back right
	normalIndexArray->push_back(4);
	normalIndexArray->push_back(2);
	normalIndexArray->push_back(1);
	// top back left
	normalIndexArray->push_back(4);
	normalIndexArray->push_back(3);
	normalIndexArray->push_back(1);



	ref_ptr<Geometry> geoMur = new Geometry;
	geoMur->setVertexArray(mur);

	geoMur->setColorBinding(Geometry::BIND_PER_VERTEX);
	geoMur->setColorArray(colorArray);

	geoMur->setNormalArray(normalArray);
	geoMur->setNormalIndices(normalIndexArray);
	geoMur->setNormalBinding(Geometry::BIND_PER_VERTEX);
	
	geoMur->addPrimitiveSet(drawElements);

	// Définit deux lumières et associe chacune d'elle à une source d'éclairage
	ref_ptr<Light> light0 = new Light;
	light0->setLightNum(0);
	light0->setPosition(Vec4((w-sc)/2,(w-sc)/2,5,1)); // Source directionnelle
	light0->setAmbient(Vec4(0.0,0.0,0.0,1));
	light0->setDiffuse(Vec4(0.1,0.1,0.1,1));
	light0->setSpecular(Vec4(1,1,1,1));
	
	ref_ptr<Light> light1 = new Light;
	light1->setLightNum(1);
	light1->setPosition(Vec4((w-sc)/2,(w-sc)/2,4,1)); // Source positionnelle
	light1->setAmbient(Vec4(0.1,0.1,0.1,1));
	light1->setDiffuse(Vec4(0.8,0.8,0.8,1));
	light1->setSpecular(Vec4(1,1,1,1));
	light1->setConstantAttenuation(0.5);
	/*
	Ruby	0.1745	0.01175	0.01175	1	0.61424	0.04136	0.04136	1	0.727811	0.626959	0.626959	1	76.8
Silver	0.19225	0.19225	0.19225	1	0.50754	0.50754	0.50754	1	0.508273	0.508273	0.508273	1	51.2
Turquoise	0.1	0.18725	0.1745	1	0.396	0.74151	0.69102	1	0.297254	0.30829	0.306678	1	12.8
*/
	// glass
	ref_ptr<Material> m1 = new Material;
	m1->setAmbient(Material::FRONT_AND_BACK,Vec4(0.1,0.18725,0.1745,1));
	m1->setDiffuse(Material::FRONT_AND_BACK,Vec4(0.396,0.74151,0.69102,1));
	m1->setSpecular(Material::FRONT_AND_BACK,Vec4(0.297254,0.30829,0.306678,1));
	m1->setShininess(Material::FRONT_AND_BACK,12.8);

	// Définit le matérial "ruby"
	ref_ptr<Material> m2 = new Material;
	m2->setAmbient(Material::FRONT_AND_BACK,Vec4(0.1745,0.01175,0.1175,1));
	m2->setDiffuse(Material::FRONT_AND_BACK,Vec4(0.61424,0.04136,0.04136,1));
	m2->setSpecular(Material::FRONT_AND_BACK,Vec4(0.727811,0.626959,0.626959,1));
	m2->setShininess(Material::FRONT_AND_BACK,76.8);

	// Définit un "blender" pour la transparence
	ref_ptr<BlendFunc> blender = new BlendFunc;
	blender->setFunction(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

	ref_ptr<LightSource> source1 = new LightSource;
	source1->setLight(light0);
	source1->setReferenceFrame(LightSource::ABSOLUTE_RF); // la position ne light0 ne subit pas les transformations géométriques de noeuds parents
	
	ref_ptr<LightSource> source2 = new LightSource;
	source2->setLight(light1);	

	// Définition d'un viewer et d'une fenêtre d'écran associée
	ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
	viewer->setUpViewInWindow(32, 32, 800, 880);
	// Désactive le "manipulator" par défaut du viewer
	viewer->getCamera()->setAllowEventFocus(false);
	ref_ptr<Camera> cameraPlateau = new Camera;
	CameraUpdate::initInstance(cameraPlateau);
	
	// Attache les deux sources d'éclairage au graphe de scène par le biais du noeud "camera"
	cameraPlateau->addChild(source1);
	cameraPlateau->addChild(source2);
	cameraPlateau->getOrCreateStateSet()->setMode(GL_LIGHT0,StateAttribute::ON); // Active la lumière light0
	cameraPlateau->getOrCreateStateSet()->setMode(GL_LIGHT1,StateAttribute::ON); // Active la lumière light1

	
	cameraPlateau->setViewport(new Viewport(0,40,820,800)); // complémentaire de la viewport de l'autre caméra ...
	cameraPlateau->setReferenceFrame(Camera::ABSOLUTE_RF);
	cameraPlateau->setClearColor(blanc);

	ref_ptr<Geode> plateau = new Geode;
	ref_ptr<ShapeDrawable> board = new ShapeDrawable;
	
	// définit le rectangle de plateau 
	board->setShape(new Box(Vec3(h/2-2*sb-sc/2,h/2-2*sb-sc/2,-hb/2),h,h,hb));
	board->setColor(couleurPlateau);
	plateau->addDrawable(board);
	cameraPlateau->addChild(plateau);
	//crée la texture des cases
	ref_ptr<Image> textureImg (osgDB::readImageFile("marble.png"));
	ref_ptr<Texture2D> texture = new Texture2D;
	texture->setImage(textureImg);	
	texture->setWrap(Texture::WRAP_S,Texture::CLAMP);
	texture->setWrap(Texture::WRAP_T,Texture::CLAMP);
	ref_ptr<ShadeModel> sm = new ShadeModel;
	sm->setMode(ShadeModel::FLAT);
	ref_ptr<TexEnv> blendEnv = new osg::TexEnv();
	blendEnv->setMode(osg::TexEnv::DECAL);
	
	plateau->getOrCreateStateSet()->setAttribute(sm.get());
	plateau->getOrCreateStateSet()->setAttribute(blendEnv.get());
	plateau->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture.get());

	//définit les cases
	ref_ptr<Geode> cases = new Geode;
	StateSet* stateset = cases->getOrCreateStateSet();
	stateset->setMode(GL_LIGHTING,StateAttribute::ON);
	// Active le mode de transparence pour les drawables du noeud
	stateset->setRenderingHint(StateSet::TRANSPARENT_BIN);
	stateset->setAttributeAndModes(blender);
	ref_ptr<ShapeDrawable> aCase = new ShapeDrawable;
	aCase->setShape(new Box(Vec3(0,0,hc/2), sc, sc, hc));
	aCase->setColor(couleurCase);
	cases->addDrawable(aCase.get());

	//appel des transformations nécessaires pour positionner les cases
	//ajoute n MatrixTransform à la caméra plateau ayant chacune comme enfant une case
	Transformations::getInstance()->trans_cases(cameraPlateau,cases);


	// create white material

Material *material = new Material();

	
	//creations des murs
	ref_ptr<Geode> murs = new Geode;
	murs->addDrawable(geoMur.get());
	
	//appel des transformations nécessaires pour positionner les murs
	// ajoute n MatrixTransform à la caméra plateau
	Transformations::getInstance()->trans_murs(cameraPlateau,murs);

	// création des rangements pour les murs
	ref_ptr<Geode> rangements = new Geode;
	 stateset = rangements->getOrCreateStateSet();
	stateset->setMode(GL_LIGHTING,StateAttribute::ON);
	// Active le mode de transparence pour les drawables du noeud
	stateset->setRenderingHint(StateSet::TRANSPARENT_BIN);
	stateset->setAttributeAndModes(blender);
	ref_ptr<ShapeDrawable> aRangement = new ShapeDrawable;
	aRangement->setShape(new Box(Vec3(0,-sb/2,hc/2),sc,2*sc+ss,hc));
	aRangement->setColor(couleurRangement);
	rangements->addDrawable(aRangement.get());

	// transformations nécessaires aux rangements cf. cases
	Transformations::getInstance()->trans_rangements(cameraPlateau,rangements);


	//creation du pion rouge
	//  O <- sphere
	// /_\ <- cone
	// deux Shape drawable pour faire le pion

	ref_ptr<Geode> pionRouge = new Geode;
	stateset = pionRouge->getOrCreateStateSet();
	stateset->setMode(GL_LIGHTING,StateAttribute::ON);
	stateset->setAttribute(m2);
	// Active le mode de transparence pour les drawables du noeud
	stateset->setRenderingHint(StateSet::TRANSPARENT_BIN);
	stateset->setAttributeAndModes(blender);
	ref_ptr<ShapeDrawable> teteRouge = new ShapeDrawable;
	ref_ptr<ShapeDrawable> corpsRouge = new ShapeDrawable;
	teteRouge->setShape(new Sphere(Vec3(0,0,sc+hc+sc/2),sc/3));
	corpsRouge->setShape(new Cone(Vec3(0,0,sc/2+hc),sc/3,sc));
	teteRouge->setColor(rouge);
	corpsRouge->setColor(rouge);
	pionRouge->addDrawable(corpsRouge);
	pionRouge->addDrawable(teteRouge);

	//creation du pion bleu
	//  O <- sphere
	// /_\ <- cone
	// deux Shape drawable pour faire le pion
	ref_ptr<Geode> pionBleu = new Geode;
	stateset = pionBleu->getOrCreateStateSet();
	stateset->setMode(GL_LIGHTING,StateAttribute::ON);
	stateset->setAttribute(m1);
	// Active le mode de transparence pour les drawables du noeud
	stateset->setRenderingHint(StateSet::TRANSPARENT_BIN);
	stateset->setAttributeAndModes(blender);
	ref_ptr<ShapeDrawable> teteBleu = new ShapeDrawable;
	ref_ptr<ShapeDrawable> corpsBleu = new ShapeDrawable;
	teteBleu->setShape(new Sphere(Vec3(0,0,sc+hc+sc/2),sc/3));
	corpsBleu->setShape(new Cone(Vec3(0,0,sc/2+hc),sc/3,sc));
	teteBleu->setColor(bleu);
	corpsBleu->setColor(bleu);
	pionBleu->addDrawable(corpsBleu);
	pionBleu->addDrawable(teteBleu);

	// Envoi vers MatrixPions -> Matrices dérivées pour pouvoir faire une méthode rafraichissement
	// suivant le pattern listener, celles-ci permettront les déplacements
	ref_ptr<MatrixPions> t_rouge = new MatrixPions(pionRouge,true);
	ref_ptr<MatrixPions> t_bleu = new MatrixPions(pionBleu,false);

	// Ajout au Listener des matrices
	PionsListener::getInstance()->add(t_rouge);
	PionsListener::getInstance()->add(t_bleu);

	// Lier les pions à des Controleurs clavier
	t_bleu->setEventCallback(new KeyboardController);
	t_rouge->setEventCallback(new KeyboardController);

	// Lier à la caméra
	cameraPlateau->addChild(t_rouge);
	cameraPlateau->addChild(t_bleu);

	/* centre caméra 
	Utilisé pour débugguer

	ref_ptr<ShapeDrawable> c = new ShapeDrawable;
	c->setShape(new Sphere(Vec3((w-sc)/2,(h/4)+(ss/2),0),1));
	c->setColor(ROUGE);

	ref_ptr<Geode> centre = new Geode;
	centre->addDrawable(c);
	cameraPlateau->addChild(centre);

	*/
	// Associe les noeuds contenant le message et le bouton graphique à la caméra2 
	// Associe un "event callback" à la caméra : gestion de l'utilisation du bouton graphique
	cameraPlateau->setEventCallback(new PickButtonController);

	Vec3 eye2 = Vec3(0,0,1.5);
	Vec3 center2 = Vec3(0,0,0);
	Vec3 up2 = Vec3(0,1,0);
	ref_ptr<Camera> camera2 = new Camera;
	camera2->setViewMatrixAsLookAt(eye2,center2,up2);
	camera2->setProjectionMatrixAsOrtho(0,40,-0.5,1,0.5,5);
	camera2->setViewport(new Viewport(0,0,800,40)); // complémentaire de la viewport de l'autre caméra ...
	camera2->setReferenceFrame(Camera::ABSOLUTE_RF);
	camera2->setClearColor(Vec4(0,0,0,1));
	
	// Conteneur pour osgText
	ref_ptr<Geode> message = new Geode;
	// osgtext défini dans le modèle car comportement douteux lorsqu'on des test avaient été fait pour respecter le pattern listener
	message->addDrawable(MYModel::getInstance()->gettexte().get());
	camera2->addChild(message);

	Vec3 eye3 = Vec3(0,0,1.5);
	Vec3 center3 = Vec3(0,0,0);
	Vec3 up3 = Vec3(0,1,0);

	// Définition des textes statiques
	// Chargement de la police
	ref_ptr<osgText::Font> maPolice = osgText::readFontFile("fonts/consolas.ttf"); // True Type Font
	ref_ptr<osgText::Font> MaPoliceWing = osgText::readFontFile("fonts/arialuni.ttf"); // True Type Font

	//Enables Unicode
	setlocale(LC_ALL, "en_US.UTF-8");


	ControlsView* cv = ControlsView::getInstance();
	ref_ptr<Camera> cameraMenu = cv->getCamera();
	cameraMenu->setViewport(new Viewport(0,840,800,40));
	cameraMenu->setClearColor(blanc);
	//zoom
	cameraMenu->setProjectionMatrixAsOrtho(0,800/2,0,40/2,0,10);
	cameraMenu->addEventCallback(new MenuController);
	// Création d'un noeud racine de type Group chapeautant les 2 caméras
	ref_ptr<Group> root = new Group;
	root->addChild(cameraPlateau.get());
	root->addChild(camera2.get());
	root->addChild(cameraMenu.get());
	// Démarrage du graphe de scène au noeud correspondant à la caméra 
	viewer->setSceneData(root.get());

	// Lancement de la boucle de rendu associée au viewer défini 
	return viewer.get();

}
